/*
=======================================================================

  Referee - BOTS
  
=======================================================================
*/
#include "g_local.h"
#include "bg_local.h"
#include "bots_local.h"
#include "bots_ref.h"

// Some static states...
static int		iMuteFlags = 0;
static qboolean	bMatchMode = qfalse;

void BOTS_refMatchmode(int clientNum) {
	gentity_t	*referee = g_entities + clientNum;
	char		*name = referee->client?referee->client->pers.netname:"unkown";


	bMatchMode = !bMatchMode;
	if (bMatchMode) {
		trap_SendServerCommand( -1 , va("cp \"^7referee %s^7 turned match mode on\n\"",name) );
		trap_SendConsoleCommand( EXEC_APPEND, "vstr matchmode_on\n");
	}
	else {
		trap_SendServerCommand( -1 , va("cp \"^7referee %s^7 turned match mode off\n\"",name) );
		trap_SendConsoleCommand( EXEC_APPEND, "vstr matchmode_off\n");
	}

	BOTS_logRefereeCommands(referee,qtrue,"ref_freeze",bMatchMode?"ON":"OFF");
}

// Called from G_Say()
qboolean BOTS_isMutedByRef(gentity_t *ent) {
	if (!ent->client)
		return qfalse;

	if (iMuteFlags != 0) { // No need to check if nobody's muted
		if (BOTS_isReferee(ent->client->ps.clientNum))
			return qfalse;
		else {
			if (iMuteFlags & (1<<ent->client->sess.sessionTeam))
				return qtrue;
		}
	}
	return qfalse;
}

void BOTS_refUnmute(int clientNum) {
	char	
		arg1[MAX_STRING_TOKENS],
		sReferee[MAX_NAME_LENGTH],
		*comment,
		*target;
	gentity_t	
		*referee = g_entities + clientNum;

	if (trap_Argc() < 2)
		goto error;

	if (!referee->client)
		goto error;

	trap_Argv(1, arg1, sizeof(arg1));
	switch (arg1[0]) {
	case 'r':
	case 'R':
		iMuteFlags	&= ~(1<<TEAM_RED);
		target		= "Red team";
		break;
	case 'b':
	case 'B':
		iMuteFlags	&= ~(1<<TEAM_BLUE);
		target		= "Blue team";
		break;
	case 's':
	case 'S':
		iMuteFlags	&= ~(1<<TEAM_SPECTATOR);
		target		= "Spectator team";
		break;
	case 'a':
	case 'A':
		iMuteFlags	&= ~((1<<TEAM_RED)|(1<<TEAM_BLUE)|(1<<TEAM_SPECTATOR));
		target		= "Everyone";
		break;
	default:
		goto error;
	}
	comment = va("'%s' unmuted (flag %i)",arg1,iMuteFlags);
	BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_unmute",comment);

	strncpy(sReferee, referee->client->pers.netname,MAX_NAME_LENGTH);
	Q_CleanStr(sReferee);
	comment = va("^7%s^7 was unmuted by referee %s(%i)",target,sReferee);
	BOTS_cp_console(-1,comment);
	return;

error:
		BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_unmute","invalid specification");
		BOTS_cp_console(clientNum,"invalid specification");

}

void BOTS_refMute(int clientNum) {
	char	
		arg1[MAX_STRING_TOKENS],
		sReferee[MAX_NAME_LENGTH],
		*comment,
		*target;
	gentity_t	
		*referee = g_entities + clientNum;

	if (trap_Argc() < 2)
		goto error;

	if (!referee->client)
		goto error;

	trap_Argv(1, arg1, sizeof(arg1));
	switch (arg1[0]) {
	case 'r':
	case 'R':
		iMuteFlags	|= 1<<TEAM_RED;
		target		= "Red team";
		break;
	case 'b':
	case 'B':
		iMuteFlags	|= 1<<TEAM_BLUE;
		target		= "Blue team";
		break;
	case 's':
	case 'S':
		iMuteFlags |= 1<<TEAM_SPECTATOR;
		target		= "Spectator team";
		break;
	case 'a':
	case 'A':
		iMuteFlags	|= (1<<TEAM_RED)|(1<<TEAM_BLUE)|(1<<TEAM_SPECTATOR);
		target		= "Everyone";
		break;
	default:
		goto error;
	}
	comment = va("'%s' muted (flag %i)",arg1,iMuteFlags);
	BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_mute",comment);

	strncpy(sReferee, referee->client->pers.netname,MAX_NAME_LENGTH);
	Q_CleanStr(sReferee);
	comment = va("^7%s^7 was muted by referee %s",target,sReferee);
	BOTS_cp_console(-1,comment);
	return;

error:
		BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_mute","invalid specification");
		BOTS_cp_console(clientNum,"invalid specification");

}


void BOTS_refClasses(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	int			i;
	int			teamdata[2][9];
	gentity_t*	player;
	char*		stats;

	if (!ent->client)
		return;

	for (i = 0; i < 9; i++)
	{
		teamdata[0][i] = 0;
		teamdata[1][i] = 0;
	}

	for (i = 0; i < level.maxclients; i++)
	{
		player = &g_entities[i];
		if (!player->client)
			continue;
		if (!player->inuse)
			continue;
		if (player->client->sess.sessionTeam == TEAM_SPECTATOR)
			continue;

		teamdata[player->client->sess.sessionTeam-1][player->bots_class-1]++;
	}

	stats =   va("Class           Female Male\n");
	stats = va("%s-----           ------ ----\n", stats);
	stats = va("%sCaptain         %3i    %3i \n", stats, teamdata[1][0], teamdata[0][0]);
	stats = va("%sBodyguard       %3i    %3i \n", stats, teamdata[1][1], teamdata[0][1]);
	stats = va("%sSniper          %3i    %3i \n", stats, teamdata[1][2], teamdata[0][2]);
	stats = va("%sSoldier         %3i    %3i \n", stats, teamdata[1][3], teamdata[0][3]);
	stats = va("%sBerzerker       %3i    %3i \n", stats, teamdata[1][4], teamdata[0][4]);
	stats = va("%sInfiltrator     %3i    %3i \n", stats, teamdata[1][5], teamdata[0][5]);
	stats = va("%sKamikazee       %3i    %3i \n", stats, teamdata[1][6], teamdata[0][6]);
	stats = va("%sNurse           %3i    %3i \n", stats, teamdata[1][7], teamdata[0][7]);
	stats = va("%sScientist       %3i    %3i \n", stats, teamdata[1][8], teamdata[0][8]);
	stats = va("bots_table \"%s\"", stats);

	trap_SendServerCommand( clientNum, stats);
}

char *BOTS_util_class2string(int num) {
	switch (num) {
	case BOTS_CAPTAIN:
		return "captain";
	case BOTS_BODYGUARD:
		return "bodyguard";
	case BOTS_SNIPER:
		return "sniper";
	case BOTS_SOLDIER:
		return "soldier";
	case BOTS_BERZERKER:
		return "berzerker";
	case BOTS_INFILTRATOR:
		return "infiltrator";
	case BOTS_KAMIKAZEE:
		return "kamikazee";
	case BOTS_NURSE:
		return "nurse";
	case BOTS_SCIENTIST:
		return "scientist";
	default:
		return "N/A";
	}
}
char *BOTS_util_team2string(int team) {
	switch (team) {
	case TEAM_RED:
		return "red ";
	case TEAM_BLUE:
		return "blue";
	default:
		return "spec";
	}
}

char *BOTS_util_formatNumber(int num) {
	if (num > 9)
		return "%i";

	return " %i";
}

// Get 1 arg from a command and return an integer (if parsing was ok)
int BOTS_util_getnumericarg() {
	char fromUI[MAX_TOKEN_CHARS];

	
	if (trap_Argc() != 2)
		goto error;
	else {	
		char	*p = fromUI;
		int		i;
		
		trap_Argv( 1, fromUI, sizeof( fromUI ) );
		for (i=0; i<sizeof(fromUI); i++) {
			if (!*p)
				break;
			else
				if ((*p < '0') || (*p >'9'))
					goto error;
				p++;
		}
		if (p == fromUI)
			goto error;
	}
	return atoi(fromUI);

error:
	return INT_MAX;
}

void BOTS_refStatus(int clientNum)
{
	gentity_t	*ent = g_entities;
	char		*out;
	int			i;

	
	out = va("Id Name           Lvl Team Class\n");
	for (i = 0; i < level.maxclients; ent++,i++) {
		char
			sNetname[MAX_NAME_LENGTH],
			sID[MAX_NAME_LENGTH],
			sLevel[MAX_NAME_LENGTH];
		int j;
				


		if (!ent->inuse)
			continue;
		if (!ent->client)
			continue;

		// Name
		strncpy(sNetname, ent->client->pers.netname,MAX_NAME_LENGTH);
		Q_CleanStr(sNetname);
		for (j=strlen(sNetname); j<15; j++)
			sNetname[j] = ' ';
		sNetname[15] = '\0';

		// Client #
		Com_sprintf(sID,MAX_NAME_LENGTH,BOTS_util_formatNumber(ent->client->ps.clientNum),ent->client->ps.clientNum);
		// Level #
		Com_sprintf(sLevel,MAX_NAME_LENGTH,BOTS_util_formatNumber(ent->client->pers.classlevel), ent->client->pers.classlevel);
		out = va("%s%s %s %s %s %s\n", out,
			sID,
			sNetname,
			sLevel,
			BOTS_util_team2string(ent->bots_team),
			BOTS_util_class2string(ent->bots_class)
			);
	}
	out = va("bots_table \"%s\"", out);
	trap_SendServerCommand( clientNum, out);
	BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_status","done");
}

void BOTS_refKick(int clientNum) {
	int			iKick = BOTS_util_getnumericarg();
	gentity_t	
		*referee = g_entities + clientNum,
		*kicked;


	if ((iKick == INT_MAX) || (iKick < 0))
		goto error;
	
	kicked = g_entities + iKick;
	if (!kicked->client || !kicked->inuse)
		goto error;
	else {
		char	
			*comment ,
			sReferee[MAX_NAME_LENGTH],
			sKicked[MAX_NAME_LENGTH];

		trap_DropClient(iKick,"ref_kick");

		strncpy(sKicked, kicked->client->pers.netname,MAX_NAME_LENGTH);
		Q_CleanStr(sKicked);
		strncpy(sReferee, referee->client->pers.netname,MAX_NAME_LENGTH);
		Q_CleanStr(sReferee);

		comment = va("%i[%s] kicked",iKick,sKicked);
		BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_kick",comment);

		comment = va("^7%s^7 was kicked by referee ^7%s^7",sKicked,sReferee);
		BOTS_cp_console(-1,comment);
	}
	return;

error:
		BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_kick","invalid client number");
		BOTS_cp_console(clientNum,"invalid client number");
}

void BOTS_refMap(int clientNum) {
	// this allows a player to change maps, but not upset the map rotation
	char
		arg1[MAX_STRING_TOKENS],
		nextmap[MAX_STRING_CHARS],
		mapcmd[MAX_STRING_CHARS];

	if (trap_Argc() != 2)
		goto error;

	trap_Argv( 1, arg1, sizeof( arg1 ) );
	if (strchr( arg1, ';') || strlen(arg1) < 1)
		goto error;

	trap_Cvar_VariableStringBuffer( "nextmap", nextmap, sizeof(nextmap) );
	if (*nextmap) {
		Com_sprintf( mapcmd, sizeof( mapcmd ), "map %s; set nextmap \"%s\"", arg1, nextmap );
	} else {
		Com_sprintf( mapcmd, sizeof( mapcmd ), "map %s", arg1 );
	}
	
	BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_map",arg1);
	trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", mapcmd ) );
	return;

error:
		BOTS_logRefereeCommandsbynum(clientNum,qtrue,"ref_map","invalid map specification");
		BOTS_cp_console(clientNum,"invalid map specification");
}

void BOTS_logRefereeCommands(gentity_t *self, qboolean bValidRef, char *cmd, char *comment) 
{
	char		netname[MAX_NAME_LENGTH];


	strncpy(netname, self->client?self->client->pers.netname:"an alien",MAX_NAME_LENGTH);
	Q_CleanStr(netname);

	G_LogPrintf("REF:%i%s[%s]: '%s', %s\n",
		self->client->ps.clientNum,
		bValidRef?" ":"!",
		netname,
		cmd,
		comment?comment:"no comment"
		);
}

void BOTS_logRefereeCommandsbynum(int clientNum, qboolean bValidRef, char *cmd, char *comment) {
	gentity_t	*self = g_entities + clientNum;

	BOTS_logRefereeCommands(self,bValidRef,cmd,comment);
}

/////////////////////////////////////////////
//
// BOTS_RefereeCommands
//
// This will parse the passed cmd string and call to the correct functions.
// 
// RETURNS
//    qfalse : cmd is not a Referee command
//	  qtrue	 : cmd is a Referee command
//
// NOTE: only valid referees are accepted
// Please log those commands
qboolean BOTS_RefereeCommands(int clientNum, char *cmd)
{
	gentity_t	*self = g_entities + clientNum;


	if (Q_stricmp (cmd, "ref_status") == 0 )
	{
		BOTS_refStatus(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_freeze", strlen ("ref_freeze")) == 0)
	{
		BOTS_toggleFrozenMode(self);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_kick", strlen ("ref_kick")) == 0)
	{
		BOTS_refKick(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_map", strlen ("ref_map")) == 0)
	{
		BOTS_refMap(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_mute", strlen ("ref_mute")) == 0)
	{
		BOTS_refMute(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_unmute", strlen ("ref_unmute")) == 0)
	{
		BOTS_refUnmute(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_matchmode", strlen ("ref_matchmode")) == 0)
	{
		BOTS_refMatchmode(clientNum);
		return qtrue;
	}
	else if (Q_strncmp (cmd, "ref_classes", strlen ("ref_classes")) == 0)
	{
		BOTS_refClasses(clientNum);
		return qtrue;
	}
	

	BOTS_logRefereeCommandsbynum(clientNum,qtrue,cmd,"invalid command");
	return qfalse;
}

// Returns whether or not this client is a referee
qboolean BOTS_isReferee(int clientNum) {
	char
		userinfo[MAX_INFO_STRING],
		*value;


	trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
	
	// check for a password
	value = Info_ValueForKey (userinfo, "ref_password");
	
	if (Q_stricmp(g_ref_password.string, "none") == 0|| Q_strncmp( g_ref_password.string, value, 9999) != 0)
		return qfalse;		
	else 
		return qtrue;
}
